package com.github.appintro;

import com.github.appintro.indicator.DotIndicatorController;
import com.github.appintro.indicator.IndicatorController;
import com.github.appintro.indicator.ProgressIndicatorController;
import com.github.appintro.internal.AppIntroViewPager;
import com.github.appintro.internal.LayoutUtil;
import com.github.appintro.internal.PermissionWrapper;
import com.github.appintro.internal.viewpager.PagerAdapter;
import com.github.appintro.utile.ResUtil;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.PageSlider;
import ohos.agp.components.ProgressBar;
import ohos.agp.window.service.WindowManager;
import ohos.bundle.IBundleManager;
import ohos.multimodalinput.event.KeyEvent;
import ohos.utils.PacMap;
import ohos.vibrator.agent.VibratorAgent;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AppIntroBase extends Ability implements AppIntroViewPagerListener {
    private final int DEFAULT_SCROLL_DURATION_FACTOR = 1;
    private final long DEFAULT_VIBRATE_DURATION = 20L;
    private final int PERMISSIONS_REQUEST_ALL_PERMISSIONS = 1;
    private final String ARG_BUNDLE_COLOR_TRANSITIONS_ENABLED = "colorTransitionEnabled";
    private final String ARG_BUNDLE_CURRENT_ITEM = "currentItem";
    private final String ARG_BUNDLE_IS_BUTTONS_ENABLED = "isButtonsEnabled";
    private final String ARG_BUNDLE_IS_FULL_PAGING_ENABLED = "isFullPagingEnabled";
    private final String ARG_BUNDLE_IS_INDICATOR_ENABLED = "isIndicatorEnabled";
    private final String ARG_BUNDLE_IS_SKIP_BUTTON_ENABLED = "isSkipButtonsEnabled";
    private final String ARG_BUNDLE_PERMISSION_MAP = "permissionMap";
    private final String ARG_BUNDLE_RETAIN_IS_BUTTONS_ENABLED = "retainIsButtonsEnabled";
    private final String ARG_BUNDLE_SLIDES_NUMBER = "slidesNumber";
    /**
     * The layout ID that will be used during inflation.
     */
    public int layoutId;
    /**
     * A subclass of [IndicatorController] to show the progress.
     * If not provided will be initialized with a [DotIndicatorController]
     */
    protected IndicatorController indicatorController;

    public boolean isButtonsEnabled() {
        return isButtonsEnabled;
    }

    public void setButtonsEnabled(boolean buttonsEnabled) {
        isButtonsEnabled = buttonsEnabled;
        updateButtonsVisibility();
    }

    public abstract void setLayoutId();

    public boolean isSkipButtonEnabled() {
        return isSkipButtonEnabled;
    }

    public void setSkipButtonEnabled(boolean skipButtonEnabled) {
        isSkipButtonEnabled = skipButtonEnabled;
        updateButtonsVisibility();
    }

    public boolean isWizardMode() {
        return isWizardMode;
    }

    public void setWizardMode(boolean wizardMode) {
        isWizardMode = wizardMode;
        isSkipButtonEnabled = !wizardMode;
        updateButtonsVisibility();
    }

    public boolean isIndicatorEnabled() {
        return isIndicatorEnabled;
    }

    public void setIndicatorEnabled(boolean indicatorEnabled) {
        isIndicatorEnabled = indicatorEnabled;
        isVisible(indicatorContainer, indicatorEnabled);
    }

    /**
     * Toggles all the buttons visibility (between visible and invisible).
     */
    protected boolean isButtonsEnabled = true;

    /**
     * Toggles only the SKIP button visibility (allows the user to skip the Intro).
     */
    protected boolean isSkipButtonEnabled = true;

    /**
     * Toggles the Wizard mode (back button instead of skip).
     */
    protected boolean isWizardMode = false;

    /**
     * Toggles the [IndicatorController] visibility.
     */
    protected boolean isIndicatorEnabled = true;

    /**
     * Toggles leaving the device's software/hardware back button.
     * If set to true, the device's back button will be ignored.
     * Note, that does does NOT lock swiping back through the slides.
     */
    protected boolean isSystemBackButtonLocked = false;

    /**
     * Toggles color cross-fading between slides.
     * Please note that slides should implement [SlideBackgroundColorHolder]
     */
    protected boolean isColorTransitionsEnabled = false;

    /**
     * Vibration duration in milliseconds
     */
    protected long vibrateDuration = DEFAULT_VIBRATE_DURATION;
    protected boolean isVibrate = false;
    private PagerAdapter pageSliderProvider;
    private AppIntroViewPager pager;
    private int slidesNumber = 0;
    private int savedCurrentItem = 0;
    private int currentlySelectedItem = -1;
    private List<Component> fragments = new ArrayList<>();
    private Component nextButton;
    private Component doneButton;
    private Component skipButton;
    private Component backButton;
    private DirectionalLayout indicatorContainer;
    private int currentSlideNumber;

    /**
     * HashMap that contains the [PermissionWrapper] objects
     */
    private Map<Integer, PermissionWrapper> permissionsMap = new HashMap<>();
    private boolean retainIsButtonsEnabled = true;
    private VibratorAgent vibrator;
    public boolean isRtl;

    @Override
    protected void onStart(Intent savedInstanceState) {
        super.onStart(savedInstanceState);
        isRtl = true;
        this.indicatorController = new DotIndicatorController(this);
        this.showStatusBar(false);
        setUIContent(layoutId);
        indicatorContainer = (DirectionalLayout) this.findComponentById(ResourceTable.Id_indicator_container);
        if (indicatorContainer != null) {
            nextButton = findComponentById(ResourceTable.Id_next);
            doneButton = findComponentById(ResourceTable.Id_done);
            skipButton = findComponentById(ResourceTable.Id_skip);
            backButton = findComponentById(ResourceTable.Id_back);
            if (skipButton instanceof Button) {
                ((Button) skipButton).setText(ResourceTable.String_app_intro_skip_button);
            }
            if (doneButton instanceof Button) {
                ((Button) doneButton).setText(ResourceTable.String_app_intro_done_button);
            }
            vibrator = new VibratorAgent();
            pager = (AppIntroViewPager) findComponentById(ResourceTable.Id_view_pager);
            doneButton.setClickedListener(
                    new AppIntroBase.NextSlideOnClickListener(true)
            );
            nextButton.setClickedListener(
                    new AppIntroBase.NextSlideOnClickListener(false)
            );
            backButton.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    pager.goToPreviousSlide();
                }
            });
            skipButton.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    dispatchVibration();
                    onSkipPressed(pager.getComponentAt(pager.getCurrentPage()));
                }
            });
            pager.addOnPageChangeListener(new OnPageChangeListener());
            pager.setOnNextPageRequestedListener(this);
            setScrollDurationFactor(DEFAULT_SCROLL_DURATION_FACTOR);
        }
    }

    protected void addSlide(Component fragment) {
        if (isRtl) {
            fragments.add(0, fragment);
            isRtl = false;
        } else {
            fragments.add(fragment);
        }
        LayoutUtil.allCount = fragments.size();
        pageSliderProvider = new PagerAdapter(fragments);
        pager.setProvider(pageSliderProvider);
        pager.setCurrentItem(0);
        pageSliderProvider.notifyDataChanged();
    }


    public void askForPermissions(String[] permissions, int slideNumber, boolean required) {
        if (slideNumber <= 0) {
            throw new IllegalStateException("Invalid Slide Number:");
        } else {
            permissionsMap.put(slideNumber, new PermissionWrapper(permissions, slideNumber, required));
        }
    }

    /**
     * Moves the AppIntro to the previous slide
     */
    protected void goToPreviousSlide() {
        pager.goToPreviousSlide();
    }

    /**
     * Moves the AppIntro to the next slide
     */
    protected void goToNextSlide() {
        boolean isLastSlide = pager.isLastSlide(fragments.size());
        if (isLastSlide) {
            onIntroFinished();
        } else {
            pager.goToNextSlide();
            onNextSlide();
        }
    }

    protected void setStatusBarColor(int color) {
        getWindow().clearFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS);
        getWindow().addFlags(WindowManager.LayoutConfig.MARK_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        getWindow().setStatusBarColor(color);
    }

    protected void setStatusBarColorRes(int color) {
        setStatusBarColor(ResUtil.getColor(this, color));
    }

    protected void setNavBarColor(int color) {
        getWindow().setNavigationBarColor(color);

    }

    protected void setNavBarColorRes(int color) {
        getWindow().setNavigationBarColor(ResUtil.getColor(this, color));
    }

    protected void showStatusBar(boolean show) {
        if (show) {
            this.getWindow().clearFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
        } else {
            this.getWindow().setWindowFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN, WindowManager.LayoutConfig.MARK_FULL_SCREEN);
        }
    }

    protected void setSwipeLock(boolean lock) {
        if (lock) {
            this.retainIsButtonsEnabled = this.isButtonsEnabled;
            this.setButtonsEnabled(true);
        } else {
            this.setButtonsEnabled(this.retainIsButtonsEnabled);
        }
        pager.setFullPagingEnabled(!lock);
    }

    protected void setProgressIndicator() {
        this.indicatorController = new ProgressIndicatorController(this, (AttrSet) null);
    }

    protected void setIndicatorColor(int selectedIndicatorColor, int unselectedIndicatorColor) {
        if (indicatorController != null) {
            indicatorController.setSelectedIndicatorColor(selectedIndicatorColor);
            indicatorController.setUnselectedIndicatorColor(unselectedIndicatorColor);
        }
    }

    protected void setScrollDurationFactor(int factor) {
        pager.setScrollDurationFactor(factor);
    }

    public void onUserRequestedPermissionsDialog() {
        requestPermissions();
    }

    protected void onUserDisabledPermission(String permissionName) {
    }

    protected void onUserDeniedPermission(String permissionName) {
    }

    protected void onPageSelected(int position) {
    }

    protected void onDonePressed(Component currentFragment) {
    }

    protected void onNextPressed(Component currentFragment) {
    }

    protected void onSkipPressed(Component currentFragment) {
    }

    protected void onNextSlide() {
    }

    protected void onIntroFinished() {
    }

    protected void onSlideChanged(Component oldFragment, Component newFragment) {
    }

    protected void onPostStart(PacMap savedInstanceState) {
        super.onPostStart(savedInstanceState);
        slidesNumber = fragments.size();
        initializeIndicator();
        retainIsButtonsEnabled = isButtonsEnabled;
        if (LayoutUtil.isRtl(getContext())) {
            pager.setCurrentItem(fragments.size() - this.savedCurrentItem);
        } else {
            pager.setCurrentItem(savedCurrentItem);
        }
        getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                Component fragment = pager.getComponentAt(pager.getCurrentPage());
                if (fragment != null) {
                    dispatchSlideChangedCallbacks(null, pager.getComponentAt(pager.getCurrentPage()));
                } else {
                    terminateAbility();
                }
            }
        });
    }

    public void onSaveAbilityState(PacMap outState) {
        super.onSaveAbilityState(outState);
        outState.putIntValue(ARG_BUNDLE_SLIDES_NUMBER, this.slidesNumber);
        outState.putBooleanValue(ARG_BUNDLE_RETAIN_IS_BUTTONS_ENABLED, this.retainIsButtonsEnabled);
        outState.putBooleanValue(ARG_BUNDLE_IS_BUTTONS_ENABLED, this.isButtonsEnabled);
        outState.putBooleanValue(ARG_BUNDLE_IS_SKIP_BUTTON_ENABLED, this.isSkipButtonEnabled);
        outState.putBooleanValue(ARG_BUNDLE_IS_INDICATOR_ENABLED, this.isIndicatorEnabled);
        outState.putIntValue(ARG_BUNDLE_CURRENT_ITEM, pager.getCurrentPage());
        outState.putBooleanValue(ARG_BUNDLE_IS_FULL_PAGING_ENABLED, pager.isFullPagingEnabled());
        outState.putSerializableObject(ARG_BUNDLE_PERMISSION_MAP, (Serializable) this.permissionsMap);
        outState.putBooleanValue(ARG_BUNDLE_COLOR_TRANSITIONS_ENABLED, this.isColorTransitionsEnabled);
    }

    public void onRestoreAbilityState(PacMap savedInstanceState) {
        super.onRestoreAbilityState(savedInstanceState);
        slidesNumber = savedInstanceState.getIntValue(ARG_BUNDLE_SLIDES_NUMBER);
        retainIsButtonsEnabled = savedInstanceState.getBooleanValue(ARG_BUNDLE_RETAIN_IS_BUTTONS_ENABLED);
        setButtonsEnabled(savedInstanceState.getBooleanValue(ARG_BUNDLE_IS_BUTTONS_ENABLED));
        setSkipButtonEnabled(savedInstanceState.getBooleanValue(ARG_BUNDLE_IS_SKIP_BUTTON_ENABLED));
        setIndicatorEnabled(savedInstanceState.getBooleanValue(ARG_BUNDLE_IS_INDICATOR_ENABLED));
        savedCurrentItem = savedInstanceState.getIntValue(ARG_BUNDLE_CURRENT_ITEM);
        pager.setFullPagingEnabled(savedInstanceState.getBooleanValue(ARG_BUNDLE_IS_FULL_PAGING_ENABLED));
        permissionsMap = (HashMap) savedInstanceState.getSerializable(ARG_BUNDLE_PERMISSION_MAP).get();
        isColorTransitionsEnabled = savedInstanceState.getBooleanValue(ARG_BUNDLE_COLOR_TRANSITIONS_ENABLED);
    }

    private void initializeIndicator() {
        if (indicatorController instanceof ProgressBar) {
            DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(150, 20);
            ((ProgressBar) indicatorController).setLayoutConfig(layoutConfig);
        }
        indicatorContainer.addComponent(indicatorController != null ? indicatorController.newInstance(this) : null);
        if (indicatorController != null) {
            indicatorController.initialize(this.slidesNumber);
            indicatorController.selectPosition(this.currentlySelectedItem);
        }

    }

    public boolean onKeyDown(int code, KeyEvent event) {
        if (code == KeyEvent.KEY_ENTER ||
                code == KeyEvent.KEY_DPAD_CENTER) {
            boolean isLastSlide = pager.isLastSlide(this.fragments.size());
            this.goToNextSlide();
            if (isLastSlide) {
                this.onDonePressed(pager.getComponentAt(pager.getCurrentPage()));
            }
            return false;
        }
        return super.onKeyDown(code, event);
    }

    public void onBackPressed() {
        if (!isSystemBackButtonLocked) {
            if (pager.isFirstSlide(this.fragments.size())) {
                super.onBackPressed();
            } else {
                pager.goToPreviousSlide();
            }
        }
    }

    private boolean getVisible(Component view) {
        return view.getVisibility() == Component.VISIBLE;
    }

    private void isVisible(Component view, boolean visible) {
        boolean isLastSlide = pager.isLastSlide(this.fragments.size());
        if (!isLastSlide) {
            doneButton.setVisibility(Component.HIDE);
        } else {
            doneButton.setVisibility(Component.VISIBLE);
            nextButton.setVisibility(Component.HIDE);
        }
        if (visible) {
            view.setVisibility(Component.VISIBLE);
        } else {
            if (view.getVisibility() == Component.VISIBLE) {
                view.setVisibility(Component.INVISIBLE);
            }
        }
    }

    private void updateButtonsVisibility() {
        if (isButtonsEnabled) {
            boolean isLastSlide = pager.isLastSlide(this.fragments.size());
            boolean isFirstSlide = pager.isFirstSlide(this.fragments.size());
            isVisible(nextButton, !isLastSlide);
            isVisible(doneButton, isLastSlide);
            isVisible(skipButton, isSkipButtonEnabled && !isLastSlide);
            isVisible(backButton, isWizardMode && !isFirstSlide);
        } else {
            isVisible(nextButton, false);
            isVisible(doneButton, false);
            isVisible(skipButton, false);
            isVisible(backButton, false);
        }
    }

    public void onIllegallyRequestedNextPage() {
        Object currentFragment = pageSliderProvider.getItem(pager.getCurrentPage());
        if (currentFragment instanceof SlidePolicy && !((SlidePolicy) currentFragment).isPolicyRespected()) {
            ((SlidePolicy) currentFragment).onUserIllegallyRequestedNextPage();
        }
    }

    private void dispatchVibration() {
        if (this.isVibrate) {
            vibrator.start((int) vibrateDuration, null);
        }
    }

    @Override
    public boolean onCanRequestNextPage() {
        boolean result;
        Object currentFragment = pageSliderProvider.getItem(pager.getCurrentPage());
        if (currentFragment instanceof SlidePolicy && !((SlidePolicy) currentFragment).isPolicyRespected()) {
            result = false;
        } else {
            result = true;
        }
        return result;
    }

    private final int getCurrentSlideNumber() {
        return pager.getCurrentSlideNumber(fragments.size());
    }

    private final boolean shouldRequestPermission() {
        return this.permissionsMap.containsKey(getCurrentSlideNumber());
    }

    private final class NextSlideOnClickListener implements Component.ClickedListener {
        private boolean isLastSlide;

        public void onClick(Component view) {
            dispatchVibration();
            if (!onCanRequestNextPage()) {
                onIllegallyRequestedNextPage();
                return;
            }
            if (shouldRequestPermission()) {
                requestPermissions();
                return;

            }
            Component currentFragment = pager.getComponentAt(pager.getCurrentPage());
            if (this.isLastSlide) {
                onDonePressed(currentFragment);
            } else {
                onNextPressed(currentFragment);
            }
            goToNextSlide();
        }

        public NextSlideOnClickListener(boolean isLastSlide) {
            this.isLastSlide = isLastSlide;
        }
    }

    public void requestPermissions() {
        setSwipeLock(true);
        PermissionWrapper permissionToRequest = permissionsMap.get(this.getCurrentSlideNumber());
        List<String> needPermissions = getDeniedPermissions(permissionToRequest.getPermissions());
        if (needPermissions == null || needPermissions.size() == 0) {
            // 已经拥有授权
            setSwipeLock(false);
            goToNextSlide();
        } else {
            requestPermissionsFromUser(needPermissions.toArray(new String[needPermissions.size()]),
                    PERMISSIONS_REQUEST_ALL_PERMISSIONS);
        }
    }

    public List<String> getDeniedPermissions(String[] permissions) {
        List<String> needRequestPermissionList = new ArrayList<>();
        // 检查是否已经具有权限
        for (int i = 0; i < permissions.length; i++) {
            if (verifySelfPermission(permissions[i]) != IBundleManager.PERMISSION_GRANTED) {
                needRequestPermissionList.add(permissions[i]);
            }
        }
        return needRequestPermissionList;
    }

    @Override
    public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
        this.setSwipeLock(false);
        boolean hasPermissionDismiss = false; // 有权限没有通过
        if (requestCode == PERMISSIONS_REQUEST_ALL_PERMISSIONS) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] == IBundleManager.PERMISSION_GRANTED) {
                    hasPermissionDismiss = true;
                    break;
                }
            }
            if (hasPermissionDismiss) { // 如果有没有被允许的权限
                permissionsMap.remove(currentSlideNumber);
                goToNextSlide();
            } else {
                // 权限已经都通过了，可以将程序继续打开了
                for (String permission : permissions) {
                    handleDeniedPermission(permission);
                }
                pager.reCenterCurrentSlide();
            }
        }
    }

    protected void handleDeniedPermission(String permission) {
        if (verifySelfPermission(permission) == IBundleManager.PERMISSION_GRANTED) {
            onUserDeniedPermission(permission);
            PermissionWrapper permissionWrapper = permissionsMap.get(getCurrentSlideNumber());
            if (permissionWrapper.getRequired()) {
                permissionsMap.remove(permissionWrapper.getPosition());
                goToNextSlide();
            }
        } else {
            onUserDeniedPermission(permission);
        }
    }

    private void performColorTransition(Component currentSlide, Component nextSlide, float positionOffset) {
    }

    private void dispatchSlideChangedCallbacks(Component oldFragment, Component newFragment) {
        if (oldFragment instanceof SlideSelectionListener) {
            ((SlideSelectionListener) oldFragment).onSlideDeselected();
        }
        if (newFragment instanceof SlideSelectionListener) {
            ((SlideSelectionListener) newFragment).onSlideSelected();
        }
        this.onSlideChanged(oldFragment, newFragment);
    }

    private final boolean isPermissionSlide() {
        return permissionsMap.containsKey(pager.getCurrentSlideNumber(fragments.size()));
    }

    public class OnPageChangeListener implements PageSlider.PageChangedListener {
        @Override
        public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
            if (isColorTransitionsEnabled && position < pager.getChildCount() - 1) {
                Component currentSlide = pager.getComponentAt(position);
                Component nextSlide = pager.getComponentAt(position + 1);
                performColorTransition(currentSlide, nextSlide, positionOffset);
            }
        }

        @Override
        public void onPageSlideStateChanged(int position) {
        }

        @Override
        public void onPageChosen(int position) {
            if (slidesNumber >= 1) {
                if (indicatorController != null) {
                    indicatorController.selectPosition(position);
                }
            }
            updateButtonsVisibility();
            pager.setPermissionSlide(isPermissionSlide());
            AppIntroBase.this.onPageSelected(position);
            if (slidesNumber > 0) {
                if (currentlySelectedItem == -1) {
                    dispatchSlideChangedCallbacks(null, pager.getComponentAt(position));
                } else {
                    dispatchSlideChangedCallbacks(pager.getComponentAt(AppIntroBase.this.currentlySelectedItem), pager.getComponentAt(pager.getCurrentPage()));
                }
            }
            currentlySelectedItem = position;
        }
    }
}
